package edu.osu.cse.c651.cryptochess;

import java.io.Serializable;
import java.net.InetAddress;

public class User implements Serializable {
	/**
	 * The serial ID for this class.
	 */
	private static final long serialVersionUID = -6200677381258858901L;
	/**
	 * The username used by the user represented by this object.
	 */
	private String username;
	/**
	 * The number of cryptochess wins this user has.
	 */
	private int wins;
	/**
	 * The number of cryptochess losses this user has.
	 */
	private int losses;
	/**
	 * The number of cryptochess draws this user has.
	 */
	private int draws;
	/**
	 * The elo rating of this user, calculated by using the algorithm found on
	 * http://www.chesselo.com/.
	 */
	private int rating;
	/**
	 * The enum value for the icon used by the user.
	 */
	private UserIcon icon;
	/**
	 * The ip address associated with this user. This is an optional field.
	 */
	private InetAddress ip;
	/**
	 * The password for the user, only set when adding a user to the database
	 */
	private String password;
	/**
	 * Color of the pieces the user should use. Used to tell opponent their color.
	 */
	private Color playerColor;
	
	/**
	 * Constructs a default user object with u as its username. This user has a
	 * new record and a white pawn for its user icon.
	 * @param u the username for this user
	 */
	public User(String u) {
		username = u;
		wins = 0;
		losses = 0;
		rating = 1000;
		icon = UserIcon.pawn_white;
	}
	
	/**
	 * Constructs a user object with the parameters specified.
	 * 
	 * @param u the username
	 * @param w the number of wins this user has
	 * @param l the number of losses this user has
	 * @param d the number of draws this user has
	 * @param r the rating of this user
	 * @param i the icon this user uses
	 */
	public User(String u, int w, int l, int d, int r, UserIcon i) {
		username = u;
		wins = w;
		losses = l;
		draws = d;
		rating = r;
		icon = i;
	}
	
	/**
	 * Returns the username of this user.
	 * @return the username of this user
	 */
	public String getUsername() {
		return username;
	}
	
	/**
	 * Returns the number of wins this user has.
	 * @return number of wins
	 */
	public int getWins() {
		return wins;
	}
	
	/**
	 * Returns the number of losses this user has.
	 * @return number of losses
	 */
	public int getLosses() {
		return losses;
	}
	
	/**
	 * Returns the number of draws this user has.
	 * @return number of draws
	 */
	public int getDraws() {
		return draws;
	}
	
	/**
	 * Returns the rating of this user.
	 * @return user rating
	 */
	public int getRating() {
		return rating;
	}
	
	/**
	 * Sets the IP address of this user.
	 * @param addr this user's ip address
	 */
	public void setIpAddress(InetAddress addr) {
		ip = addr;
	}
	
	/**
	 * Returns the IP address of this user.
	 * @return user's ip address
	 */
	public InetAddress getIpAddress() {
		return ip;
	}
	
	/**
	 * Sets the user icon for this user.
	 * @param u the user icon
	 */
	public void setUserIcon(UserIcon u) {
		icon = u;
	}
	
	/**
	 * Returns the user icon for this user
	 * @return user icon
	 */
	public UserIcon getUserIcon() {
		return icon;
	}
	
	/**
	 * Sets the password for this user. The password should be in plain text
	 * for use by the database helper for login and adding new accounts.
	 * @param pass password
	 */
	public void setPassword(String pass) {
		password = pass;
	}
	
	/**
	 * Returns the password for this user. This field is generally null.
	 * @return password
	 */
	public String getPassword() {
		return password;
	}
	
	/**
	 * Sets the color of the player this user represents.
	 * @param c Chess piece color
	 */
	public void setColor(Color c) {
		playerColor = c;
	}
	
	/**
	 * Returns the color of the player this user represents.
	 * @return chess piece color
	 */
	public Color getColor() {
		return playerColor;
	}
	
	/**
	 * Returns a string representation of this user.
	 */
	@Override
	public String toString() {
		return username + ":" + wins + ":" + losses + ":" + draws + ":" + rating + ":" + icon.name() + ":";
	}
	
	/**
	 * Checks if the users are equivalent.
	 */
	@Override
	public boolean equals(Object o) {
		if(o.getClass().equals(this.getClass())) {
			User u = (User)o;
			if(u.username.equals(this.username)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Parses the user from its string representation.
	 * @param user the string to parse
	 * @return the user object represented by this string
	 */
	public static User parseUser(String user) {
		User result = null;
		String[] arr = user.split(":");
		
		if(arr.length == 6) {
			result = new User(arr[0], 
							  Integer.parseInt(arr[1]), 
							  Integer.parseInt(arr[2]), 
							  Integer.parseInt(arr[3]), 
							  Integer.parseInt(arr[4]),
							  UserIcon.valueOf(arr[5]));
		}
		
		return result;
	}
	
	/**
	 * Records the result of a game and updates the rating based off ELO calculations.
	 * 
	 * @param result if this user won, lost, or drew
	 * @param opponent the opponent this user played
	 */
	public void recordResult(GameResult result, User opponent) {
		int ratingDiff = Math.abs(rating - opponent.getRating());
		double expectedResult = 0.00;
		double actualResult = 0.00;
		int kfactor = 10;
		
		// Record the changes in the win-loss record
		switch(result) {
		case win:
			wins++;
			actualResult = 1.00;
			break;
		case draw:
			draws++;
			actualResult = 0.50;
			break;
		case loss:
			losses++;
			actualResult = 0.00;
			break;
		}
		
		// Calculate the new elo rating
		if(rating > opponent.getRating()) {
			if(ratingDiff > 735) {
				expectedResult = 1.00;
			} else if (ratingDiff > 500) {
				expectedResult = 0.99;
			} else if (ratingDiff > 450) {
				expectedResult = 0.96;
			} else if (ratingDiff > 400) {
				expectedResult = 0.94;
			} else if (ratingDiff > 350) {
				expectedResult = 0.92;
			} else if (ratingDiff > 300) {
				expectedResult = 0.89;
			} else if (ratingDiff > 250) {
				expectedResult = 0.85;
			} else if (ratingDiff > 200) {
				expectedResult = 0.81;
			} else if (ratingDiff > 150) {
				expectedResult = 0.76;
			} else if (ratingDiff > 100) {
				expectedResult = 0.70;
			} else if (ratingDiff > 50) {
				expectedResult = 0.64;
			} else if (ratingDiff > 25) {
				expectedResult = 0.57;
			} else {
				expectedResult = 0.53;
			}
		} else {
			if(ratingDiff > 735) {
				expectedResult = 0.00;
			} else if (ratingDiff > 500) {
				expectedResult = 0.01;
			} else if (ratingDiff > 450) {
				expectedResult = 0.04;
			} else if (ratingDiff > 400) {
				expectedResult = 0.06;
			} else if (ratingDiff > 350) {
				expectedResult = 0.08;
			} else if (ratingDiff > 300) {
				expectedResult = 0.11;
			} else if (ratingDiff > 250) {
				expectedResult = 0.15;
			} else if (ratingDiff > 200) {
				expectedResult = 0.19;
			} else if (ratingDiff > 150) {
				expectedResult = 0.24;
			} else if (ratingDiff > 100) {
				expectedResult = 0.30;
			} else if (ratingDiff > 50) {
				expectedResult = 0.36;
			} else if (ratingDiff > 25) {
				expectedResult = 0.43;
			} else if (ratingDiff > 0 ) {
				expectedResult = 0.47;
			} else {
				expectedResult = 0.50;
			}
		}
		
		if((wins + draws + losses) > 30) {
			kfactor = 10;
		} else if((wins + draws + losses) > 20) {
			kfactor = 20;
		} else {
			kfactor = 30;
		}
		
		rating = (int)((double)kfactor * (actualResult - expectedResult));
	}
}
